/**
 * Collaborative Story Development Web Application (CSDApp) 
 * Copyright Anton Strack 2014
 *
 * This file is part of Collaborative Story Development Application (CSDApp).
 *
 * CSDApp is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or any later version.
 *
 * CSDApp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * CSDApp. If not, see <http://www.gnu.org/licenses/>.
 *
 */
package csdwa;

import java.sql.Connection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpSession;

/**
 * This class deals with authorization for the csdwa application. It will
 * determine the ownership between a user and a resource and it will provide the
 * access to the acl lists and their isAllowed method.
 *
 * @author Anton Strack
 */
public class Authorizer {

    private Connection conn = null;
    private HttpSession session = null;
    private Map<String, Acl> acl = new HashMap<String, Acl>();
    private int userRoleId;
    private int resourceTypeId;

    public Authorizer(Connection conn) {
        this.conn = conn;
    }

    /**
     * Checks if the action on the resource type is authorized for the role and
     * their ownership status. A database request will be made to determine
     * ownership status of the resource to the user.
     *
     * @param userId
     * @param userRoleId
     * @param actionId
     * @param resourceTypeId
     * @param resourceId
     * @return
     */
    public boolean isAuthorized(int userId, int userRoleId, int actionId, int resourceTypeId, int resourceId) {
        int ownership = this.getOwnership(userId, resourceTypeId, resourceId);
        return this.checkAcl(userRoleId, actionId, resourceTypeId, ownership);
    }

    /**
     * Checks if the action on the resource type is authorized for the role
     * assuming the resource is not owned by the user. No ownership check is
     * made to the database.
     *
     * @param userRoleId
     * @param actionId
     * @param resoruceTypeId
     * @return
     */
    public boolean isAuthorizedNotOwned(int userRoleId, int actionId, int resourceTypeId) {
        int NOT_OWNED = 0;
        return this.checkAcl(userRoleId, actionId, resourceTypeId, NOT_OWNED);
    }

    /**
     * Check if the current user id is the creator and thus owner of the
     * resource.
     *
     * @param userId the id of the user
     * @param resourceTypeId the type of resource to check the ownership of
     * @param resourceId the id of the resource to check
     * @return 1 if the user owns the resource and 0 if not.
     */
    public int getOwnership(int userId, int resourceTypeId, int resourceId) {
        int owned = 0;
        CsdwaDao<Post> dao = new DaoFactory(conn).getDao(resourceTypeId);
        if (dao != null) {
            int exists = dao.selectBeanById(resourceId);
            if (exists > 0 && dao.getBean() instanceof Post) {
                Post post = dao.getBean();
                if (post.getCreatedById() == userId) {
                    owned = 1;
                }
            } else {
                System.out.println("dao is:" + dao + " so set ownership to owned.");
                owned = 1;
            }
        }
        return owned;
    }

    /**
     * populate the acl map with all acl rows from the acl table.
     */
    public void populateAcl() {
        AclDao aclDao = new AclDao(this.conn);
        aclDao.selectBeans();
        this.loadAcl(aclDao.getList());
    }

    /**
     * Populate the acl map with all rows containing the passed in roleId.
     *
     * @param roleId the role id to match all acl rows with.
     */
    public void populateAcl(int roleId) {
        AclDao aclDao = new AclDao(this.conn);
        aclDao.selectBeans(roleId);
        this.loadAcl(aclDao.getList());
    }

    /**
     * Populate the acl map with all rows containing the passed in roleId and
     * resourceTypeId.
     *
     * @param roleId
     * @param resourceTypeId
     */
    public void populateAcl(int roleId, int resourceTypeId) {
        AclDao aclDao = new AclDao(this.conn);
        aclDao.selectBeans(roleId, resourceTypeId);
        this.loadAcl(aclDao.getList());
    }

    /**
     * Sets the primary resource type id for use in querying the acl table for
     * related rows.
     *
     * @param resourceTypeId the resource id used in querying the acl table.
     */
    public final void setResourceTypeId(int resourceTypeId) {
        this.resourceTypeId = resourceTypeId;
    }

    /**
     * Sets the user role the access control list is for. Each ACl implementing
     * object is specific to one user role.
     *
     * @param userRoleId the role id used in querying the acl table
     */
    public final void setUserRoleId(int userRoleId) {
        this.userRoleId = userRoleId;
    }

    /**
     * Get the primary resource type id used for querying the acl table.
     *
     * @return the primary resource type id
     */
    public int getResourceTypeId() {
        return this.resourceTypeId;
    }

    /**
     * Get the user role id used for querying the acl table and used by the
     * isAllowed() method
     *
     * @return the user role id
     */
    public int getUserRoleId() {
        return this.userRoleId;
    }

    private void loadAcl(List<Acl> acls) {
        for (Acl al : acls) {
            StringBuilder keySb = new StringBuilder();
            keySb.append(al.getResourceTypeId()).append(al.getActionId()).append(al.getRoleId()).append(al.getOwnership());
            this.acl.put(keySb.toString(), al);
        }
    }

    private boolean checkAcl(int userRoleId, int actionId, int resourceTypeId, int ownership) {
        boolean authorized = false;

        populateAcl(userRoleId, resourceTypeId);// should rethink this, maybe allow access control to be passed around

        StringBuilder keySb = new StringBuilder();
        keySb.append(resourceTypeId).append(actionId).append(userRoleId).append(ownership);
        String key = keySb.toString();
        if (this.acl.containsKey(key) && this.acl.get(key) != null) {
            authorized = this.acl.get(key).getAccess();
        }
        //  System.out.println("checkAcl with resourceTypeId:" + resourceTypeId + " and actionTypeId:" + actionId + " and userRoleId:" + userRoleId + " and ownership:" + ownership + " is authorized:" + authorized);
        return authorized;
    }
}
